Jeudis du Libre in Brussels
Hudson 1.0
Jenkins
Jenkins 2.0: Pipeline and Blue Ocean plugins
Declarative Pipeline (vs. Scripted Pipeline), Pipeline Editor
Jenkinsfile always commited back to the codebase of the project.
Declarative Pipeline |
Scripted Pipeline |
|---|---|
On top of |
Full power of |
Consistent structure |
Greater liberty |
Human readable |
Complex |
Lower barrier entry |
Groovy "Expert" needed |
UI Editor including syntax validation |
Frequent obscure syntax errors |
Validation of syntax at very beginning of a build |
Error during the build |
Error point to the point in the pipeline which is causing problem |
Difficult to find source of error |
CodePipeline
Hooks as code (YAML)
Gitlab, Bitbucket…)
Build stage
bitbucket-pipelines.yml
Platform.sh sticks to the git vocabulary (hooks)
YAML code
pipeline {
agent any
stages {
stage('Build') {
steps {
sh '''
rm -f *.html
asciidoc --backend list | grep deckjs || asciidoc --backend install deckjs-1.6.3.zip
for file in *.asciidoc
do
asciidoc $file
done
'''
}
}
stage('Tests') {
steps {
sh 'file pipeline-as-code.html'
}
}
stage('Deploy') {
steps {
echo 'Hello World'
}
}
}
post {
always {
archive '*.html'
echo 'I will always say Hello again!'
}
}
}
Source: https://jenkins.io/blog/2017/03/16/fosdem-event-report/
pipeline {
agent {
docker "fstab/asciidoc"
}
}
pipeline {
agent any
environment {
DISABLE_AUTH = 'true'
DB_ENGINE = 'sqlite'
AWS_ACCESS_KEY_ID = credentials('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = credentials('AWS_SECRET_ACCESS_KEY')
}
options {
buildDiscarder(logrotator(numToKeepStr:'5'))
timeout(time: 30, unit: 'MINUTES')
}
parameters {
string(name: 'TARGET',
description: "Where we're deploying to",
)
}
stages {
stage('Build') {
steps {
sh 'printenv'
}
}
}
}
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'echo "Hello World"'
sh '''
echo "Multiline shell steps works too"
ls -lah
'''
}
}
}
}
pipeline {
agent any
stages {
stage('Deploy') {
steps {
when {
branch "*/master"
}
retry(3) {
sh './flakey-deploy.sh'
}
timeout(time: 3, unit: 'MINUTES') {
sh './health-check.sh'
}
}
}
}
}
pipeline {
agent any
stages {
stage('Test') {
steps {
sh 'echo "Fail!"; exit 1'
}
}
}
post {
always {
echo 'This will always run'
archive '*.html'
}
success {
echo 'This will run only if successful'
}
failure {
echo 'This will run only if failed'
mail to: 'team@example.com',
subject: "Failed Pipeline: ${currentBuild.fullDisplayName}",
body: "Something is wrong with ${env.BUILD_URL}"
}
unstable {
echo 'This will run only if the run was marked as unstable'
}
changed {
echo 'This will run only if the state of the Pipeline has changed'
echo 'For example, if the Pipeline was previously failing but is now successful'
}
}
}
// Wait outside an executor context
input "Do you want to continue?"
node('php') {
// Do something heavy
}
try {
// do something
}
catch (e) {
// In case of failure
throw e
}
finally {
// Success or failure, always do ...
}
milestone 10
notify_build('STARTED')
build_project()
milestone 20
deploy_now('dev')
milestone 30
/*
* The workflow: Gitflow.
* On branch develop, deploy automatically to development.
* On master branch, deploy automatically to QA, and wait for a validation before deploy to production.
* Obsolete builds are canceled.
* Externalize the content of the build and deploy tasks.
*/
try {
node('php') {
milestone 10
notify_build('STARTED')
build_project()
milestone 20
switch ( "${env.BRANCH_NAME}" ) {
case "develop":
milestone 30
deploy_now('dev')
milestone 40
return
case "master":
milestone 30
deploy_now('stg')
milestone 40
return
}
}
switch ( "${env.BRANCH_NAME}" ) {
case "master":
milestone 60
deploy_after_validation('prd')
milestone 80
return
}
}
catch (e) {
currentBuild.result = "FAILED"
throw e
}
finally {
// Success or failure, always send notifications
notify_build(currentBuild.result)
}
/*
* Define how to build a project.
* We assume we are already in a node context.
*/
def build_project() {
stage ("Build") {
// get some information about the build environment
echo "branch name: ${env.BRANCH_NAME}"
sh "env"
// Get versioned files
checkout scm
// Build -- adapt to your project
dir('www') {
sh "sh ../.jenkins/build.sh"
archiveArtifacts(
artifacts: '**',
fingerprint: true,
onlyIfSuccessful: true
)
}
}
}
/*
* Define how to deploy a project without waiting.
* We assume we are already in a node context.
* Deployment on corresponding environment will be attempted only if the file exist in the '.jenkins/config' directory:
* - dev.groovy
* - tst.groovy
* - stg.groovy
* - prd.groovy
*/
def deploy_now(String targetEnv = 'dev') {
stage ("Deploy to ${targetEnv}") {
go = 'undefined'
try {
// load parameters for the environment ${targetEnv}
load path: ".jenkins/${targetEnv}.groovy"
echo "Variables defined for deployment in .jenkins/${targetEnv}.groovy : " +
"credentialsID=${credentialsId}, " +
"server=${target_server}, " +
"user=${target_user}"
go = 'true'
}
catch (e) {
echo "No deployment done since the environment is not defined in .jenkins/${targetEnv}.groovy"
go = 'false'
}
//echo "${go}"
if ( "${go}"== 'true' ) {
ansiblePlaybook(
credentialsId: "${credentialsId}",
extras: "--user=${target_user} --verbose",
installation: "ansible",
inventory: "${target_server},",
playbook: ".jenkins/deploy.yml",
sudoUser: null
)
}
}
}
/*
* Define how to wait for user validation before launching a deploy.
* We assume we are outside of a node context.
*/
def deploy_after_validation(String targetEnv = 'dev') {
stage ("Validate to deploy to ${targetEnv}") {
timeout(time: 100, unit: 'DAYS') {
hipchatSend(
color: 'PURPLE',
message: "User input requested : Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
input "Deploy to ${targetEnv}?"
}
}
node('php') {
deploy_now("${targetEnv}")
}
}
/